package base 
{
	import flash.display.Bitmap;
	import flash.display.BitmapData;
	import flash.display.Graphics;
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.geom.Matrix;
	import flash.geom.Rectangle;
	
	/**
	 * Base class for sequences with single source image and XML metadata
	 * @author jvirkovskiy
	 */
	public class SingleSourceSequenceXML extends Sprite 
	{
		////////////////////////////////////////////////////
		// 
		////////////////////////////////////////////////////
		
		protected var _framesData:Vector.<FrameData>;
		
		private var _bitmapData:BitmapData;
		private var _matrix:Matrix = new Matrix();
		
		private var _frameWidth:Number;
		private var _frameHeight:Number;
		
		private var _graphics:Graphics;
		
		private var _currentFrame:int = 1;
		private var _isPlaying:Boolean = false;
		
		////////////////////////////////////////////////////
		// 
		////////////////////////////////////////////////////
		
		/**
		 * Constructor
		 * @param	data		source image
		 * @param	metadata	XML metadata
		 */
		public function SingleSourceSequenceXML(data:BitmapData, metadata:XML, initFrame:int=1, stop:Boolean=false)
		{
			super();
			
			if (!data || !metadata)
				throw Error("Data or metadata can't be null.");
			
			_bitmapData = data;
			parseMetadata(metadata);
			
			goToFrame(initFrame, stop);
		}
		
		/**
		 * Current frame
		 */
		public function get currentFrame():int
		{
			return _currentFrame;
		}
		
		/**
		 * Go to specified frame
		 * @param	frame	frame number
		 * @param	stop	stop/play flag
		 * @return	true, if success
		 */
		public function goToFrame(frame:int, stop:Boolean):Boolean
		{
			if (frame < 1 || frame >= _framesData.length)
				return false;
			
			_currentFrame = frame;
			_graphics.clear();
			
			var fd:FrameData = _framesData[_currentFrame - 1];
			
			if (fd.src)
			{
				_matrix.tx = fd.offsetX - fd.x;
				_matrix.ty = fd.offsetY - fd.y;
				_graphics.beginBitmapFill(fd.src, _matrix);
				_graphics.drawRect(fd.offsetX, fd.offsetY, fd.width, fd.height);
				_graphics.endFill();
			}
			
			if (stop)
				this.stop();
			else
				play();
			
			return true;
		}
		
		/**
		 * Start playing animation from current frame
		 */
		public function play():void
		{
			if (_isPlaying)
				return;
			
			addEventListener(Event.ENTER_FRAME, onEnterFrame, false, 0, true);
			_isPlaying = true;
		}
		
		/**
		 * Stop playing animation
		 */
		public function stop():void
		{
			if (!_isPlaying)
				return;
			
			removeEventListener(Event.ENTER_FRAME, onEnterFrame);
			_isPlaying = false;
		}
		
		/**
		 * Enter frame handler
		 * @param	event	event
		 */
		private function onEnterFrame(event:Event):void
		{
			if (!goToFrame(currentFrame + 1, false))
				goToFrame(1, false);
		}
		
		override public function get width():Number
		{
			return _frameWidth;
		}
		
		override public function get height():Number
		{
			return _frameHeight;
		}
		
		/**
		 * Parse metadata
		 * @param	data	metadata
		 */
		protected function parseMetadata(data:XML):void
		{
			_frameWidth = data.hasOwnProperty("@frame_width") ? Number(data.@frame_width) : 0;
			_frameHeight = data.hasOwnProperty("@frame_height") ? Number(data.@frame_height) : 0;
			
			var zeroX:int = data.hasOwnProperty("@zero_x") ? int(data.@zero_x) : 0;
			var zeroY:int = data.hasOwnProperty("@zero_y") ? int(data.@zero_y) : 0;
			
			if (zeroX || zeroY)
			{
				var canvas:Sprite = new Sprite();
				canvas.x = -zeroX;
				canvas.y = -zeroY;
				addChild(canvas);
				_graphics = canvas.graphics;
			}
			else
			{
				_graphics = graphics;
			}
			
			var frames:XMLList = data.frame;
			_framesData = new Vector.<FrameData>();
			
			for each (var frame:XML in frames)
			{
				if (!frame.hasOwnProperty("@index"))
					continue;
				
				var fd:FrameData = new FrameData();
				_framesData.push(fd);
				
				fd.index = int(frame.@index);
				
				if (frame.hasOwnProperty("@empty") && frame.@empty.toString() == "true")
					continue;
				
				fd.src = _bitmapData;
				fd.x = frame.hasOwnProperty("@x") ? int(frame.@x) : 0;
				fd.y = frame.hasOwnProperty("@y") ? int(frame.@y) : 0;
				fd.offsetX = frame.hasOwnProperty("@offset_x") ? int(frame.@offset_x) : 0;
				fd.offsetY = frame.hasOwnProperty("@offset_y") ? int(frame.@offset_y) : 0;
				fd.width = frame.hasOwnProperty("@width") ? int(frame.@width) : 0;
				fd.height = frame.hasOwnProperty("@height") ? int(frame.@height) : 0;
			}
			
			var sortByIndex:Function = function (a:FrameData, b:FrameData):int {
				if (a.index > b.index) return 1;
				if (a.index < b.index) return -1;
				return 0;
			};
			_framesData.sort(sortByIndex);
		}
	}
}
